package com.voicenews.aggregator;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import com.voicenews.model.Article;

public class RSSHandler extends DefaultHandler {

	// Feed and Article objects to use for temporary storage
	private Article currentArticle = new Article();
	private List<Article> articleList = new ArrayList<Article>();
	private String[] typeDate = { "EEE, dd MMM yyyy HH:mm:ss zzz",
			"yyyy-MM-dd HH:mm:ss" };
	private Date lastTime;
	private boolean isAdd = true;
	URL url = null;
	String host = null;
	StringBuffer chars = new StringBuffer();

	/*
	 * This method is called everytime a start element is found (an opening XML
	 * marker) here we always reset the characters StringBuffer as we are only
	 * currently interested in the the text values stored at leaf nodes
	 * 
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
	 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
	 */

	public void startElement(String uri, String localName, String qName,
			Attributes atts) {
		chars = new StringBuffer();
	}

	/*
	 * This method is called everytime an end element is found (a closing XML
	 * marker) here we check what element is being closed, if it is a relevant
	 * leaf node that we are checking, such as Title, then we get the characters
	 * we have accumulated in the StringBuffer and set the current Article's
	 * title to the value
	 * 
	 * If this is closing the "Item", it means it is the end of the article, so
	 * we add that to the list and then reset our Article object for the next
	 * one on the stream
	 * 
	 * 
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
	 * java.lang.String, java.lang.String)
	 */
	@SuppressWarnings("unused")
	public void endElement(String uri, String localName, String qName)
			throws SAXException {

		if (qName.equalsIgnoreCase("title")) {
			currentArticle.setTitle(chars.toString());
		} else if (qName.equalsIgnoreCase("description")) {
			if (host.contains("timviecnhanh")) {
				currentArticle.setDescription("");
			} else {
				currentArticle.setDescription(chars.toString());
			}
		} else if (qName.equalsIgnoreCase("pubDate")) {
			if (chars.toString().length() != 0) {
				try {
					@SuppressWarnings("deprecation")
					Date tmp = new Date(chars.toString().trim());
					if (tmp == null) {
						tmp = convertStringDateToLongDate(chars.toString()
								.trim());
						currentArticle.setPubDate(tmp.toString());
					} else {
						currentArticle.setPubDate(chars.toString().trim());
					}
					if (!tmp.after(lastTime)) {
						isAdd = false;
					}
				} catch (Exception e) {
					Date tmp = convertStringDateToLongDate(chars.toString()
							.trim());
					currentArticle.setPubDate(tmp.toString());
				}
			}

		} else if (qName.equalsIgnoreCase("item")) {

		} else if (qName.equalsIgnoreCase("link")) {
			System.out.println(chars.toString());
			try {
				String urlString = chars.toString();
				if (urlString
						.contains("#sa-ns_mchannel=rss&ns_source=PublicRSS20-sa")) {
					urlString.replace(
							"#sa-ns_mchannel=rss&ns_source=PublicRSS20-sa", "");
				} else {
					String host = url.getHost();
					if (!chars.toString().contains(host)) {
						urlString = host.concat(urlString);
					}
				}
				currentArticle.setUrl(new URL(urlString));
			} catch (MalformedURLException e) {
				System.out.println("malform");
				System.out.println(e.toString());
			}
		}

		// Check if looking for article, and if article is complete
		if (qName.equalsIgnoreCase("item")) {

			if (isAdd) {
				articleList.add(currentArticle);
			}
			currentArticle = new Article();
			isAdd = true;
		}
	}

	/*
	 * This method is called when characters are found in between XML markers,
	 * however, there is no guarante that this will be called at the end of the
	 * node, or that it will be called only once , so we just accumulate these
	 * and then deal with them in endElement() to be sure we have all the text
	 * 
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
	 */
	public void characters(char ch[], int start, int length) {
		chars.append(new String(ch, start, length));
	}

	/**
	 * This is the entry point to the parser and creates the feed to be parsed
	 * 
	 * @param feedUrl
	 * @return
	 */
	public List<Article> getLatestArticles(String feedUrl, String time) {
		lastTime = new Date(Long.valueOf(time).longValue());
		try {
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			url = new URL(feedUrl);
			host = url.getHost();
			HttpURLConnection urlConnection = (HttpURLConnection) url
					.openConnection();
			urlConnection
					.addRequestProperty("User-Agent",
					// "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
							"Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101");
			urlConnection.setReadTimeout(10000);
			urlConnection.setConnectTimeout(10000);
			xr.setContentHandler(this);
			InputSource is = new InputSource(urlConnection.getInputStream());
			xr.parse(is);
		} catch (IOException e) {
		} catch (SAXException e) {
		} catch (ParserConfigurationException e) {
		}
		return articleList;
	}

	public Date convertStringDateToLongDate(String string) {
		int len = typeDate.length;
		int i = 0;
		SimpleDateFormat format = null;
		Date date = null;
		do {
			try {
				format = null;
				format = new SimpleDateFormat(typeDate[i]);
				date = format.parse(string);
			} catch (ParseException e) {
			}
			i++;
		} while (date == null && i < len);

		return date;
	}

	@SuppressWarnings("unused")
	private static String convertStreamToString(InputStream is)
			throws IOException {
		Charset charset = Charset.forName("utf-8");
		InputStreamReader inputReader = new InputStreamReader(is); //
		inputReader.read();
		BufferedReader reader = new BufferedReader(inputReader);
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

}
